package ranab.jar.view;

import java.util.*;
import java.util.zip.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import ranab.jar.*;
import ranab.gui.GuiUtils;

/**
 * This tree model stores jar/zip data.
 *
 * @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
 */
public
class JarTree implements TreeModel, JarObserver, JarUI {
    
    private TreeNode mRoot;
    private TreeModelEvent mRootEvent;
    private Vector mListenrList;
    private Component mParent;
    private JTree mjTree;
    private JScrollPane mjScrollPane;
    private JPanel mjTopPane;
    
    private boolean mbIsActive; 
    
    public JarTree(Component comp)  {
        mListenrList = new Vector();
        mParent = comp;
        mbIsActive = false;
        initComponents();
    }
    
    private void initComponents()  {
        mRoot = new TreeNode(null, ".", null);
        mRootEvent = new TreeModelEvent(this, new TreePath(mRoot));
        mjTree = new JTree(this);
        mjTree.setEditable(false);
        
        JScrollPane scrollPane = new JScrollPane(mjTree, 
                                    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                                       JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        mjTopPane = new JPanel();
        mjTopPane.setLayout(new BorderLayout());
        mjTopPane.add(scrollPane);                        
    }
    
    /**
     * Initialize tree
     */
    public void start()  {
        mRoot.clear();
        structureChanged();
    }
    
    /**
     * Add new entry.
     */
    public void setNext(ZipEntry entry)  {
        String name = entry.getName();
        StringTokenizer st = new StringTokenizer(name, "/");
        
        TreeNode parent = mRoot;
        String currAbsPath = mRoot.getAbsolutePath();
        while (st.hasMoreTokens())  {
            String tok = st.nextToken();
            currAbsPath += '/' + tok;
            TreeNode thisNode = parent.getNode(currAbsPath);
            if (thisNode == null)  {
                thisNode = new TreeNode(parent, tok, entry);
                structureChanged();
            }
            parent = thisNode;
        }
    }
    
    /**
     * Set count.
     */
    public void setCount(int count)  {
    }
    
    /**
     * Jar file processing end.
     */
    public void end()  {
        structureChanged();
        mjTree.expandPath(mRootEvent.getTreePath());
    }
    
    /**
     * Update component UI.
     */
    public void updateLnF() {
        SwingUtilities.updateComponentTreeUI(mjTopPane);
    }
    
    /**
     * Get the scroll pane that contains the tree.
     */
    public JPanel getPanel() {
        return mjTopPane;
    }
 
    /**
     * Is active.
     */
    public boolean isActive()  {
        return mbIsActive;
    }      
    
    /**
     * Set enable tag.
     */
    public void setActive(boolean b)  {
        mbIsActive = b;
    }
    
    /**
     * Get selected entries.
     */
    public ZipEntry[] getSelectedEntries()  {
        TreePath selectedItems[] = mjTree.getSelectionPaths();
        if (selectedItems == null || selectedItems.length == 0)  {
            return null;
        }
        Vector vec = new Vector();
        for(int i=0; i<selectedItems.length; i++)  {
            TreeNode node = (TreeNode)selectedItems[i].getLastPathComponent();
            if(node.isLeaf()) {
                ZipEntry entry = node.getEntry();
                if(!vec.contains(entry)) {
                    vec.add(entry);
                }
            }
            else {
                getLeaves(node, vec);
            }
        }
        
        if (vec.size() == 0)  {
            return null;
        }
        
        ZipEntry[] entries = new ZipEntry[vec.size()];
        for(int i=0; i<entries.length; i++)  {
            entries[i] = (ZipEntry)vec.get(i);
        }
        return entries;
    } 
    
    /**
     * Get children entries.
     */
    private void getLeaves(TreeNode node, Vector vec) {
        
        // check size
        if( (node == null) || (node.size() == 0) ) {
            return;
        }
        
        // check zipentry
        for(int i=0; i<node.size(); i++) {
            TreeNode child = (TreeNode)node.elementAt(i);
            if(child.isLeaf()) {
                ZipEntry entry = child.getEntry();
                if(!vec.contains(entry)) {
                    vec.add(entry);
                }
            }
            else {
                getLeaves(child, vec);
            }
        }
    }
     
    
    /**
     * Send structure change event.
     */
    private void structureChanged()  {
        int sz = mListenrList.size();

        for (int i=sz-1; i>=0; i--) {
            ((TreeModelListener)mListenrList.elementAt(i)).treeStructureChanged(mRootEvent);
        } 
    }
    
    /**
     * Set error message.
     */
    public void setError(String errMsg)  {
        if (isActive())  {
            GuiUtils.showErrorMessage(mParent, errMsg);
        }
    }
    
    
    /**
     * Add a listener.
     */
    public void addTreeModelListener(TreeModelListener l) {
        mListenrList.add(l);
    }
    
    /**
     * Remove a listener.
     */
    public void removeTreeModelListener(TreeModelListener l) {
        mListenrList.remove(l);
    }
    
   /**
    * Object changed. In our case it is not possible - so
    * igmore it.
    */
    public void valueForPathChanged(TreePath path, Object newValue) {
    }
    
    /**
     * Get child at an index.
     */
     public Object getChild(Object parent, int index)  {
         return ((TreeNode)parent).get(index);
     }
     
    /**
     * Get child count.
     */
     public int getChildCount(Object parent)  {
          return ((TreeNode)parent).size();
     } 
      
     /**
      * Get root node.
      */
      public Object getRoot()  {
           return mRoot;
      }
      
     /**
      * Is a leaf
      */
      public boolean isLeaf(Object node)  {
          return ((TreeNode)node).isLeaf();
      }
      
     /**
      * Get index of child.
      */
     public int getIndexOfChild(Object parent, Object child)  {
        return ((TreeNode)parent).indexOf(child);
     }  
     
}
